package org.wu.framework.bean.factory;


import org.wu.framework.bean.definition.BeanDefinition;
import org.wu.framework.bean.definition.DefineClassCacheFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * bean 工厂
 */
public class BeanFactory {

    private final static ConcurrentHashMap<Class<?>, Object> INSTANCE_MAP = new ConcurrentHashMap<>();


    /**
     * 通过class 初始化对象
     *
     * @param clazz class
     * @param <T>   返回对象范型
     * @return clazz 对象
     */
    public static  <T>  T getInstance(Class<T> clazz) {
        if (INSTANCE_MAP.containsKey(clazz)) {
            return clazz.cast(INSTANCE_MAP.get(clazz));
        }
        return getInstance0(clazz);
    }


    /**
     * 通过class 初始化对象
     *
     * @param clazz class
     * @param <T>   返回对象范型
     * @return clazz 对象
     */
    private static  <T> T getInstance0(Class<T> clazz) {

        try {
            BeanDefinition beanDefinition = DefineClassCacheFactory.cacheBeanDefinition(clazz);
            Constructor<?> constructor = beanDefinition.getConstructor();
            Class<?>[] parameterTypes = beanDefinition.getParameterTypes();

            Object[] initArgs = new Object[parameterTypes.length];
            for (int i = 0; i < parameterTypes.length; i++) {
                initArgs[i] = getInstance(parameterTypes[i]);
            }
            Object newInstance = constructor.newInstance(initArgs);


            // 字段初始化而后注入
            List<Field> injectingFields = beanDefinition.getInjectingFields();
            for (Field injectingField : injectingFields) {
                boolean canAccess = injectingField.canAccess(newInstance);
                if(canAccess){
                    injectingField.setAccessible(true);
                }
                Class<?> injectingFieldType = injectingField.getType();
                Object injectingFieldInstance = getInstance(injectingFieldType);
                injectingField.set(newInstance,injectingFieldInstance);
            }

            // set 方法
            List<Method> injectingMethods = beanDefinition.getInjectingMethods();
            for (Method injectingMethod : injectingMethods) {
                Class<?>[] injectingMethodParameterTypes = injectingMethod.getParameterTypes();
                Object[] initInjectingMethodParameterArgs = new Object[injectingMethodParameterTypes.length];
                for (int i = 0; i < injectingMethodParameterTypes.length; i++) {
                    initInjectingMethodParameterArgs[i] = getInstance(injectingMethodParameterTypes[i]);
                }
                injectingMethod.invoke(newInstance,initInjectingMethodParameterArgs);
            }
            // 标记 bean 初始化


            INSTANCE_MAP.put(clazz, newInstance);
            return clazz.cast(newInstance);
        } catch (InstantiationException | IllegalAccessException |
                 IllegalArgumentException | InvocationTargetException e) {
            throw new IllegalArgumentException("Failed to create instance of  type " +
                    clazz.getName(), e);

        }

    }


}
